package com.loong.web.gp4j.service.user;

import java.io.IOException;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.loong.tool.code.VCode;
import com.loong.tool.code.VCode.Vc;
import com.loong.tool.lang.Strings;
import com.loong.tool.secret.security.digest.Digest;
import com.loong.web.gp4j.api.baidu.BaiduApi;
import com.loong.web.gp4j.api.baidu.BaiduResponse;
import com.loong.web.gp4j.api.baidu.response.IPData;
import com.loong.web.gp4j.bean.param.user.LoginParam;
import com.loong.web.gp4j.bean.schema.user.UserLoginSchema;
import com.loong.web.gp4j.bean.schema.user.UserSchema;
import com.loong.web.gp4j.bean.schema.user.UserStatuses;
import com.loong.web.gp4j.bean.vo.user.UserLoginVo;
import com.loong.web.gp4j.dao.user.UserDao;
import com.loong.web.gp4j.dao.user.UserLoginDao;
import com.loong.web.gp4j.service.error.ErrorService;
import com.loong.web.suppert.auth.User;
import com.loong.web.suppert.auth.UserCookies;
import com.loong.web.suppert.auth.UserSessions;
import com.loong.web.suppert.collection.list.KvnList;
import com.loong.web.suppert.collection.param.PageParam;
import com.loong.web.suppert.collection.param.ParamMap;
import com.loong.web.suppert.collection.valid.ErrorList;
import com.loong.web.suppert.collection.vo.RowsVo;

/**
 * 用户登录服务
 *
 * @author 张成轩
 */
@Service
public class UserLoginService {

	/** 是否需要验证参数名称 */
	private static final String HAS_VC = "_login_has_vc";
	/** 验证码参数名称 */
	private static final String VC = "_login_vc";

	@Resource
	private ErrorService errorService;
	@Resource
	private UserDao userDao;
	@Resource
	private UserLoginDao userLoginDao;
	@Resource(name = "loginlogThreadPool")
	private ExecutorService threadPool;

	@Resource(name = "code.auths")
	private KvnList<String, Integer, String> auths;
	@Resource(name = "user.statuses")
	private KvnList<String, Integer, String> statuses;

	@Value("${apikey.baidu}")
	private String baiduKey;
	@Resource
	private BaiduApi baiduApi;

	/** ip正则表达式 */
	private static final String REGEX_IP = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}";

	/**
	 * 判断是否有验证码
	 * 
	 * @param request 请求
	 * @return 是否有验证码
	 */
	public boolean hasVc(HttpServletRequest request) {

		HttpSession session = request.getSession();
		Boolean vc = (Boolean) session.getAttribute(HAS_VC);
		if (vc == null) {
			vc = false;
			session.setAttribute(HAS_VC, false);
		}
		return vc;
	}

	/**
	 * 用户登录
	 * 
	 * @param request 请求
	 * @param response 响应
	 * @param param 参数
	 * @param errorList 错误信息集合
	 * @return 登录是否成功
	 */
	public boolean login(HttpServletRequest request, HttpServletResponse response, LoginParam param,
			ErrorList errorList) {

		HttpSession session = request.getSession();
		// 判断是否需要验证码
		Boolean vc = (Boolean) session.getAttribute(HAS_VC);
		if (vc == null) {
			vc = true;
			session.setAttribute(HAS_VC, true);
		}
		if (vc) {
			String code = (String) session.getAttribute(VC);
			if (code == null || param.getVc() == null || !code.equalsIgnoreCase(param.getVc())) {
				// 清空验证码录入框
				param.setVc(null);
				// 返回错误信息
				errorList.addError("vc", "验证码错误");
				return false;
			}
			param.setVc(null);
		}
		// 查询用户信息
		UserSchema user = userDao.getByUserWithAuths(param.getUser());
		// 校验是否成功
		boolean succ = false;
		// 是否需要验证码
		boolean hasVc = true;
		// 校验用户状态
		if (user == null)
			errorList.addError("用户名或密码错误");
		else if (user.getStatus().equals(statuses.getV(UserStatuses.CANCEL))) {
			errorList.addError("用户已销户");
			hasVc = false;
		} else {
			// 校验密码
			String password = Digest.digest(param.getPassword());
			if (!password.equals(user.getPassword()))
				errorList.addError("用户名或密码错误");
			else {
				if (user.getStatus().equals(statuses.getV(UserStatuses.LOCK)))
					errorList.addError("用户已锁定，请联系管理员进行解锁");
				else
					succ = true;
				hasVc = false;
			}
		}
		session.setAttribute(HAS_VC, hasVc);
		param.setPassword(null);
		if (succ) {
			// 添加Session
			UserSessions.setUser(request, UserAdapter.build(user, auths));
			if (param.getRemember() != null && param.getRemember())
				// 添加Cookie
				UserCookies.setUser(request, response, user.getId().toString());
			else
				// 移除Cookie
				UserCookies.removeUser(request, response);
			saveUserLogin(request, user);
		}
		return succ;
	}

	/**
	 * 注销
	 * 
	 * @param request 请求
	 * @param response 响应
	 */
	public void logout(HttpServletRequest request, HttpServletResponse response) {

		// 移除session
		UserSessions.removeUser(request);
		// 移除cookie
		UserCookies.removeUser(request, response);
	}

	/**
	 * 获取验证码图片
	 * 
	 * @param request 请求
	 * @param response 响应
	 */
	public void vc(HttpServletRequest request, HttpServletResponse response) {

		// 创建验证码
		Vc vc = VCode.getVc(85, 32, 4);
		// 加入到Session
		request.getSession().setAttribute(VC, vc.getCode().toLowerCase());
		try {
			// 写入流
			OutputStream os = response.getOutputStream();
			VCode.write(vc.getImg(), os);
		} catch (IOException e) {
			errorService.error(e);
		}
	}

	/**
	 * 保存登录日志信息
	 * 
	 * @param request 请求
	 * @param user 用户对象
	 */
	public void saveUserLogin(HttpServletRequest request, UserSchema user) {

		// 封装对象
		UserLoginSchema userLogin = new UserLoginSchema();
		UserLoginAdapter.convert(request, user, userLogin);
		threadPool.execute(new Runnable() {

			@Override
			public void run() {

				// 获取ip信息
				IPData ipInfo = getIpInfo(userLogin.getIp());
				if (ipInfo != null)
					userLogin.setArea(Strings.join(" ", ipInfo.getCountry(), ipInfo.getProvince(), ipInfo.getCity(),
							ipInfo.getCarrier()));
				userLoginDao.insert(userLogin);
			}
		});
	}

	/**
	 * 查询用户日志集
	 * 
	 * @param request 请求
	 * @param page 分页
	 * @return 返回对象
	 */
	public RowsVo<UserLoginVo> queryUserLogin(HttpServletRequest request, PageParam page) {

		// 当前用户
		User user = UserSessions.getUser(request);
		// 查询参数
		ParamMap params = new ParamMap();
		params.setPage(page);
		params.put("userId", user.getId());
		// 查询
		List<UserLoginSchema> userLogins = userLoginDao.query(params);
		List<UserLoginVo> userLoginVos = new LinkedList<UserLoginVo>();
		// 封装对象
		UserLoginAdapter.convert(userLogins, userLoginVos);
		RowsVo<UserLoginVo> vo = new RowsVo<UserLoginVo>();
		vo.setPage(page);
		vo.setRows(userLoginVos);
		return vo;
	}

	/**
	 * 获取ip信息
	 * 
	 * @param ip ip
	 * @return ip信息
	 */
	private IPData getIpInfo(String ip) {

		if (!ip.matches(REGEX_IP))
			return null;
		BaiduResponse<IPData> response;
		try {
			response = baiduApi.ipLookup(baiduKey, ip);
		} catch (RuntimeException e) {
			return null;
		}
		if (response.getErrNum() == 0)
			return response.getRetData();
		return null;
	}
}
